package MathCore.Operators;

import MathCore.Lexeme;
import MathCore.Operand;
import MathCore.Operands.Complex;
import MathCore.Operands.List;
import MathCore.Operands.Number;
import MathCore.Operator;
import MathCore.Real;

public class ArOperator extends Operator
{
	public static final int ADDITION=0;
	public static final int SUBSTRACTION=1;
	public static final int PRODUCT=2;
	public static final int DIVISION=3;
	public static final int POWER=4;

	private int type=ADDITION;

	public ArOperator(){}
	
	public ArOperator(int t)
	{
		type = t;
	}

	public ArOperator(ArOperator orig)
	{
		type = orig.type;
	}

	public Operand exec(Operand[] operands) throws OperatorException
	{
		if(operands.length != params())
			throw new OperatorException("Неправильное количество операндов у оператора "+toString()+". Необходимо "+params()+".");

		Operand left = operands[0], right = operands[1];

		if(left == null || right == null)
			throw new OperatorException("Один или оба операнда null.");

		if(have(operands, List.class)) return expandListsLinear(operands, this);
		if(have(operands, Complex.class)) expandNumbersToComplex(operands);

		if(only(operands,Number.class))
		{
			Number result = new Number( (Number) left);
			switch(type)
			{
				case ADDITION:
					result.get().add(((Number)right).get());
					break;

				case SUBSTRACTION:
					result.get().sub(((Number)right).get());
					break;

				case PRODUCT:
					result.get().mul(((Number)right).get());
					break;

				case DIVISION:
					result.get().div(((Number)right).get());
					break;

				case POWER:
					result.get().pow(((Number)right).get());
					break;
			}
			
			return result;
		}
		if(only(operands, Complex.class))
		{
			Real a = new Real(((Complex)operands[0]).Re());
			Real b = new Real(((Complex)operands[0]).Im());
			Real c = new Real(((Complex)operands[1]).Re());
			Real d = new Real(((Complex)operands[1]).Im());
			
			switch(type)
			{
				case ADDITION:
				{
					a.add(c);
					b.add(d);
					return new Complex(a,b,Complex.RE_IM);
				}
				case SUBSTRACTION:
				{
					a.sub(c);
					b.sub(d);
					return new Complex(a,b,Complex.RE_IM);
				}
				case PRODUCT:
				{
					Real temp1 = new Real(a);	// a
					temp1.mul(c);				// ac
					Real temp2 = new Real(b);	// b
					temp2.mul(d);				// bd
					temp1.sub(temp2);			// ac - bd

					Real temp3 = new Real(b);	// b
					temp3.mul(c);				// bc
					temp2.assign(a);			// a
					temp2.mul(d);				// ad
					temp2.add(temp3);			// bc + ad

					return new Complex(temp1, temp2, Complex.RE_IM);
				}
				case DIVISION:
				{
					Real temp1 = new Real(a);	// a
					temp1.mul(c);				// ac
					Real temp2 = new Real(b);	// b
					temp2.mul(d);				// bd
					temp1.add(temp2);			// ac + bd

					Real temp3 = new Real(b);	// b
					temp3.mul(c);				// bc
					temp2.assign(a);			// a
					temp2.mul(d);				// ad
					temp3.sub(temp2);			// bc - ad

					c.sqr();					// c^2
					d.sqr();					// d^2
					c.add(d);					// c^2 + d^2

					temp1.div(c);				// (ac + bd)/(c^2 + d^2)
					temp3.div(c);				// (bc - ad)/(c^2 + d^2)

					return new Complex(temp1, temp3, Complex.RE_IM);
				}
				case POWER:
				{
					a = new Real(((Complex)operands[0]).abs());
					a.ln();
					b = new Real(((Complex)operands[0]).arg());
					TrigFunction.convertAngleToRadians(b);
					Complex z1 = new Complex(a, b, Complex.RE_IM);
					Complex z2 = new Complex(c, d, Complex.RE_IM);
					TrigFunction.convertAngleToRadians(z2.Im());
					Complex [] params = {z1, z2};
					ArOperator mul = new ArOperator(PRODUCT);
					Complex z3 = (Complex) mul.exec(params);
					z3.Re().exp();
					TrigFunction.convertAngleFromRadians(z3.Im());
					return new Complex(z3.Re(), z3.Im(), Complex.ABS_ARG);
				}
			}

			throw new OperatorException(OperatorException.UNSUPPORTED_COMBINATION);
		}
		else
		{
			throw new OperatorException(OperatorException.UNSUPPORTED_COMBINATION);
		}
	}

	public int params()
	{
		return 2;
	}

	public int priority()
	{
		switch(type)
		{
			case ADDITION:
			case SUBSTRACTION:
				return 1;

			case PRODUCT:
			case DIVISION:
				return 2;

			case POWER:
				return 3;
		}

		return 0;
	}

	public String toString()
	{
		switch(type)
		{
			case ADDITION:
				return "+";

			case SUBSTRACTION:
				return "-";

			case PRODUCT:
				return "*";

			case DIVISION:
				return "/";

			case POWER:
				return "^";
		}

		return "";
	}

	public Lexeme clone()
	{
		return new ArOperator(this);
	}

	public int getType()
	{
		return type;
	}
	
}
